package src

// 追单
import (
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"gitee.com/haifengat/goctp"
)

var (
	firstOffset   int = 0 // 首次发单偏移priceTick数
	cancelSeconds int = 3 // n 秒后不成交则撤单
	reorderTimes  int = 3 // 重试次数
	reorderOffset int = 2 // 重发偏移，以对价为基准价

	orderTimes sync.Map // 委托的追单次数
)

func init() {
	if tmp, exists := os.LookupEnv("firstOffset"); exists {
		firstOffset, _ = strconv.Atoi(tmp)
	}
	if tmp, exists := os.LookupEnv("cancelSeconds"); exists {
		cancelSeconds, _ = strconv.Atoi(tmp)
	}
	if tmp, exists := os.LookupEnv("reorderTimes"); exists {
		reorderTimes, _ = strconv.Atoi(tmp)
	}
	if tmp, exists := os.LookupEnv("reorderOffset"); exists {
		reorderOffset, _ = strconv.Atoi(tmp)
	}
}

func SetConfig(cfg TailConfig) {
	firstOffset = cfg.FirstOffset
	cancelSeconds = cfg.CancelSeconds
	reorderTimes = cfg.ReorderTimes
	reorderOffset = cfg.ReorderOffset
}

// StartOrder 委托
func StartOrder(ord *Order) error {
	logrus.Infof("%+v", ord)
	if !t.IsLogin {
		return errors.New("接口未登录")
	}
	// 执行order
	instField, ok := t.Instruments.Load(ord.Instrument)
	if !ok {
		logrus.Error("未取到合约的信息")
		return errors.New("未取到合约信息")
	}

	ord.Direction = strings.Title(ord.Direction) // 大写开头
	ord.Offset = strings.Title(ord.Offset)
	// 价格偏移
	dire := goctp.DirectionBuy
	priceOffset := float64(firstOffset) * float64(instField.(*goctp.InstrumentField).PriceTick)
	if ord.Direction == "Sell" {
		dire = goctp.DirectionSell
		priceOffset = -priceOffset // sell 减少价格
	}
	if ord.Offset == "Open" { // 开仓
		offset := goctp.OffsetFlagOpen
		orderKey := t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
		go tailOrder(orderKey, ord)
		return nil
	}
	// 处理 shfe和ine 平今
	if instField.(*goctp.InstrumentField).ExchangeID == "SHFE" || instField.(*goctp.InstrumentField).ExchangeID == "INE" {
		var key string
		if dire == goctp.DirectionBuy {
			key = fmt.Sprintf("%s_short", ord.Instrument)
		} else {
			key = fmt.Sprintf("%s_long", ord.Instrument)
		}
		pf, exists := t.Positions.Load(key)
		if !exists {
			logrus.Error(ord.Instrument, " 无持仓")
			return nil
		}
		posiField := pf.(*goctp.PositionField)
		// 无今仓：全部平昨
		if posiField.TodayPosition == 0 {
			offset := goctp.OffsetFlagCloseYesterday
			logrus.Info(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
			orderKey := t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
			go tailOrder(orderKey, ord)
		} else if ord.Volume <= posiField.TodayPosition { // 全部平今
			offset := goctp.OffsetFlagCloseToday
			logrus.Info(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
			orderKey := t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
			go tailOrder(orderKey, ord)
		} else {
			// 平今
			offset := goctp.OffsetFlagCloseToday
			logrus.Info(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), posiField.TodayPosition)
			orderKey := t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), posiField.TodayPosition)
			go tailOrder(orderKey, ord)
			// 平昨
			offset = goctp.OffsetFlagCloseYesterday
			yd := ord.Volume - posiField.TodayPosition
			logrus.Info(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), yd)
			orderKey = t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), yd)
			go tailOrder(orderKey, ord)
		}
	} else { // 平仓
		offset := goctp.OffsetFlagClose
		logrus.Info(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
		orderKey := t.ReqOrderInsert(ord.Instrument, dire, offset, ord.Price+float64(priceOffset), ord.Volume)
		go tailOrder(orderKey, ord)
	}
	return nil
}

// StartCTP 启动CTP
func regTrade() {
	t.RegOnRtnCancel(onCancel)
}

// onCancel //撤单后追单
func onCancel(field *goctp.OrderField) {
	if !field.IsLocal {
		return
	}
	// 通过对应的 orderKey 取 order 信息
	orderKey := fmt.Sprintf("%d_%s", field.SessionID, field.OrderRef)
	var ord *Order
	if tmp, ok := orderTimes.Load(orderKey); !ok {
		logrus.Error("没取到对应的 Order 信息:", orderKey)
	} else {
		ord = tmp.(*Order)
	}
	// 取 order 的追单次数
	times, _ := orderTimes.LoadOrStore(ord, int(0))
	// 追单: 即时行情
	obj, ok := q.Ticks.Load(field.InstrumentID)
	if !ok {
		logrus.Error("未收到此合约的行情：", field.InstrumentID)
		return
	}
	o, ok := t.Instruments.Load(field.InstrumentID)
	if !ok {
		logrus.Error("合约列表中不存在合约：", field.InstrumentID)
		return
	}
	inst := o.(*goctp.InstrumentField)
	tick := obj.(*goctp.TickField)
	// 读取重发次数
	var price float64
	if times.(int) < reorderTimes {
		offset := float64(reorderOffset) * inst.PriceTick
		if field.Direction == goctp.DirectionBuy {
			price = tick.BidPrice1 + offset // 在价下买入, 会一直不成交, 以测试追单是否正确.
		} else {
			price = tick.AskPrice1 - offset
		}
		// 板价控制
		price = math.Min(tick.UpperLimitPrice, price) // 避免超过板价
		price = math.Max(tick.LowerLimitPrice, price) // 避免超过板价

		price, _ = strconv.ParseFloat(fmt.Sprintf("%.5f", price), 64)
		orderTimes.Store(ord, times.(int)+1)
		logrus.Infof("reorder|%s|times:%d|price:%G", field.OrderSysID, times.(int)+1, price)
		orderKey := t.ReqOrderInsert(field.InstrumentID, field.Direction, field.OffsetFlag, price, field.VolumeLeft)
		go tailOrder(orderKey, ord)
	} else { // 市价单
		// 应用涨跌停价替代
		if field.Direction == goctp.DirectionBuy {
			price = tick.UpperLimitPrice
		} else {
			price = tick.LowerLimitPrice
		}
		price, _ = strconv.ParseFloat(fmt.Sprintf("%.5f", price), 64)
		logrus.Infof("reorder|%s|lasttime|price:%G", field.OrderSysID, price)
		t.ReqOrderInsert(field.InstrumentID, field.Direction, field.OffsetFlag, price, field.VolumeLeft)
	}
}

// tailOrder 追单
func tailOrder(orderKey string, ord *Order) {
	time.Sleep(time.Duration(cancelSeconds) * time.Second)
	// 取委托状态
	obj, ok := t.Orders.Load(orderKey)
	if !ok {
		logrus.Error("取委托状态错误：不存在", orderKey)
		return
	}
	ordField := obj.(*goctp.OrderField)
	// 已成交 或撤单
	if ordField.OrderStatus == goctp.OrderStatusAllTraded {
		logrus.Info("委托结束, 状态: AllTraded")
		return
	}
	if ordField.OrderStatus == goctp.OrderStatusCanceled {
		logrus.Info("Canceled")
		return
	}
	orderTimes.Store(orderKey, ord)
	// 撤单
	t.ReqOrderAction(orderKey)
}
